#ifndef LOCKER_H
#define LOCKER_H

#include<exception>//异常头文件
#include<pthread.h> //线程头文件
#include<semaphore.h> //信号量头文件

//RAII，构造函数中获得对象资源，析构函数中释放对象资源

class sem//信号量类
{
public:
    
    sem(){
        //初始化信号量 若不成功抛出异常
        if(sem_init(&m_sem,0,0)!=0){
            throw std::exception();
        }
    }
    sem(int num){
        //第二个参数为0表示线程间同步，非0表示进程间同步，num为信号量初始值
        if(sem_init(&m_sem,0,num)!=0){
            throw std::exception();
        }
    }
    //析构函数
    ~sem(){
        //信号量销毁
        sem_destroy(&m_sem);
    }
    //阻塞
    bool wait(){
        return sem_wait(&m_sem)==0;
    }
    //释放
    bool post(){
        return sem_post(&m_sem)==0;
    }
private:
    sem_t m_sem;
};

class locker//互斥锁类
{
public:
    locker(){
        //第二个参数表示互斥锁属性，设为NULL为默认属性，返回0表示成功初始化，否则抛出异常
        if(pthread_mutex_init(&m_mutex,NULL)!=0){
            throw std::exception();
        }
    }
    ~locker(){
        pthread_mutex_destroy(&m_mutex);
    }
    //加锁
    bool lock(){
        return pthread_mutex_lock(&m_mutex)==0;
    }
    //解锁
    bool unlock(){
        return pthread_mutex_unlock(&m_mutex)==0;
    }
private:
    pthread_mutex_t m_mutex;
};

class cond//条件变量类
{
public:
    cond(){
        //初始化条件变量，不成功则抛出异常
        if(pthread_cond_init(&m_cond,NULL)!=0){
            throw std::exception();
        }
    }
    ~cond(){
        pthread_cond_destroy(&m_cond);
    }
    //条件变量需要互斥的访问，所以需要互斥锁,cond_wait()内部实现了解锁和加锁
    //等待条件变量，当不满足时，将线程插入该变量的请求队列然后解锁
    //当传出返回值时，说明被唤醒 自动加锁
    
    bool wait(pthread_mutex_t *m_mutex){
        int ret=0;
        //pthread_mutex_lock(m_mutex);
        ret=pthread_cond_wait(&m_cond,m_mutex);//执行前必须持有锁
        ////pthread_mutex_unlock(m_mutex);
        return ret==0;
    }
    //设置等待时间，超时返回错误ETIMEDOUT
    bool timewait(pthread_mutex_t *m_mutex,struct timespec t){
        int ret=0;
        //pthread_mutex_lock(m_mutex);
        ret=pthread_cond_timedwait(&m_cond,m_mutex,&t);//执行前必须持有锁
        ////pthread_mutex_unlock(m_mutex);
        return ret==0;
    }
    //唤醒
    bool signal(){
        return pthread_cond_signal(&m_cond)==0;
    }
    //广播唤醒，唤醒所有请求队列上的线程（会出现虚假唤醒）
    bool broadcast(){
        return pthread_cond_broadcast(&m_cond)==0;
    }
private:
    pthread_cond_t m_cond;
};

#endif